﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.Data.OracleClient;
using System.Configuration;

namespace TerminologiaSinacorSrv
{
    /// <summary>
    /// Responsável pela limpeza da base de dados atual e importação 
    /// dos novos registros vindos do Access (.mdb) para o Oracle.
    /// </summary>
    public class ImportaDados
    {
        #region | Enumeradores |

        /// <summary>
        /// Tabelas do Sistema
        /// </summary>
        public enum Tabela 
        { 
            Ts2_Termo_Glossario, 
            Ts2_Termo, 
            Ts_Responsavel, 
            Ts_Fonte, 
            Ts_Tipo_Fonte, 
            Ts_Modulo, 
            Todas
        }

        #endregion

        #region | Variáveis |

        private string _arquivoMDB = string.Empty;  //Caminho completo para o arquivo MDB a importar

        #endregion

        #region | Propriedades |

        /// <summary>
        /// Recebe o caminho completo do arquivo a importar
        /// </summary>
        public string ArquivoMDB {
            set { _arquivoMDB = value; } 
            get { return _arquivoMDB; } 
        }
        
        #endregion

        #region | Construtores e Destrutores |

        /// <summary>
        /// Construtor padrão
        /// </summary>
        public ImportaDados()
        {
            //...
        }

        /// <summary>
        /// Destrutor
        /// </summary>
        ~ImportaDados()
        {
            //...
        }
        
        #endregion

        #region | Metodos Privados |
        
        /// <summary>
        /// Remove os dados antigos da tabela passada como parâmetro
        /// </summary>
        /// <param name="Tabela">Tabela a limpar os dados</param>
        /// <returns>true=OK; false=Erro</returns>
        private bool RemoveDadosAntigos(Tabela tabela)
        {
            string nomeTabela = string.Empty;                        //Nome da Tabela escolhida,com base no parâmetro passado
            string[] ArrayTabelas = null;                            //Array com nome das tabelas, para TODAS
            StringBuilder querySQL = new StringBuilder();            //Comando SQL a Executar
            int? linhasAfetadas = 0;                                 //Linhas afetadas pelo comando Oracle executado
            bool retorno = false;                                    //Retorno do processamento
            OracleTransaction transacaoOracle = null;                //Transação Oracle
            OracleConnection conexaoOracle = new OracleConnection(); //Conexão Oracle
            OracleCommand comandoOracle = new OracleCommand();       //Comando para o Oracle

            //Pega a tabela escolhida
            nomeTabela = Enum.GetName(typeof(Tabela), tabela);

            //Se escolheu "TODAS",então cria o array
            if (nomeTabela.ToUpper() == "TODAS") { ArrayTabelas = Enum.GetNames(typeof(Tabela)); }
            
            try
            {
                //Define a string de conexão para o servidor de Desenvolvimento
                conexaoOracle.ConnectionString = ConfigurationManager.AppSettings["ConnectionStringSinaTools"];

                //Abrindo a conexao com o Oracle
                conexaoOracle.Open();

                //Cria a transação
                transacaoOracle = conexaoOracle.BeginTransaction(IsolationLevel.ReadCommitted);

                //Define a conexão do comando
                comandoOracle.Connection = conexaoOracle;

                //Define a transação
                comandoOracle.Transaction = transacaoOracle;

                //Define o tipo de comando a executar
                comandoOracle.CommandType = CommandType.Text;

                //Se escolheu todas as tabelas,então deleta uma a uma, senão será pontual
                if (tabela == Tabela.Todas)
                {
                    //Varre cada nome da tabela para fazer o DELETE (se fossepelo enum,seria até: Enum.GetValues(typeof(Tabelas)).Length-1
                    for (int i = 0; i < ArrayTabelas.Length-1; i++)
                    {
                        //Define ocomando
                        comandoOracle.CommandText = "DELETE FROM " + ArrayTabelas[i].ToString();
                        
                        //Executa o comando
                        linhasAfetadas = comandoOracle.ExecuteNonQuery();
                    }
                }
                else
                {
                    //Define ocomando
                    comandoOracle.CommandText = "DELETE FROM " + nomeTabela;
                    
                    //Executa o comando
                    linhasAfetadas = comandoOracle.ExecuteNonQuery();
                }

                //Valida a transação
                transacaoOracle.Commit();

                //Retorno OK
                retorno = true;
            }
            catch (Exception ex)
            {
                //Da rollback na transação
                transacaoOracle.Rollback();
                //Avisa que deu erro
                retorno = false;
                //Lança a exceção de volta
                throw new Exception("Erro ao tentar limpar os dados antigos das tabelas!\n" + ex.Message.ToString());
            }
            finally
            {
                //Libera os recursos
                if (transacaoOracle != null) { transacaoOracle.Dispose(); }
                if (comandoOracle != null) { comandoOracle.Dispose(); }
                if (conexaoOracle.State != ConnectionState.Closed) { conexaoOracle.Close(); }
            }

            //Retorna o que ocorreu
            return retorno;
        }

        /// <summary>
        /// Formata os dados conforme o tipo para montar o Insert
        /// </summary>
        /// <param name="tipo">Tipo de Dado</param>
        /// <param name="valor">Vaor do dado,convertido em string</param>
        /// <returns>String com dado formatado para o Insert</returns>
        private string DadoFormatado(Type tipo, string valor)
        {

            string retorno = string.Empty;

            if (valor == string.Empty || valor.Replace(" ", "").Length == 0) 
            { 
                retorno = "NULL";
                return retorno;
            }

            //--> Verificando se o texto tem aspas simples
            valor=valor.Replace("'", " ");
            //
            switch (tipo.FullName)
            {
                case "System.String": retorno = "'" + valor + "'"; break;
                case "System.Char": retorno = "'" + valor + "'"; break;
                case "System.DateTime": retorno = "to_date('" + DateTime.Parse(valor).ToString("yyyy-MM-dd") + "','yyyy-MM-dd')"; break;
                case "System.Decimal": retorno = valor; break;
                case "System.Int32": retorno = valor; break;
                case "System.Boolean": if (bool.Parse(valor)==true) { retorno = "1"; } else { retorno = "0"; }; break;
                default: retorno = valor; break;
            }

            if (retorno == string.Empty || retorno.Replace(" ", "").Length == 0) { retorno = "NULL"; }
            
            return retorno;
        }


        #endregion

        #region | Metodos Publicos |

        public bool InsereDadosNovos(Tabela tabela)
        {
            string nomeTabela = string.Empty;                        //Nome da Tabela escolhida,com base no parâmetro passado
            string[] ArrayTabelas = null;                            //Array com nome das tabelas, para TODAS
            string colunas = string.Empty;                           //Nome das colunas a inserir
            string dados = string.Empty;                             //Dados da linha a inserir
            StringBuilder querySQL = new StringBuilder();            //Comando SQL a Executar
            bool retorno = false;                                     //Retorno do processamento

            OracleTransaction transacaoOracle = null;                //Transação Oracle
            OracleConnection conexaoOracle = new OracleConnection(); //Conexão Oracle
            OracleCommand comandoOracle = new OracleCommand();       //Comando para o Oracle

            OleDbConnection conexaoAccess = new OleDbConnection();   //Conexão Access
            OleDbCommand comandoAccess = new OleDbCommand();         //Comando Access
            OleDbDataReader drAccess = null;                         //DataReader Access

            //Testa se passou o arquivo a importar e se o arquivo existe, senão sai
            if ((ArquivoMDB == string.Empty) || (ArquivoMDB.Replace(" ", "").Length == 0) || (!System.IO.File.Exists(ArquivoMDB)))
            {
                //Lança a exceção avisando que o arquivo é inválido/inexistente
                throw new Exception("Arquivo inválido ou inexistente!");
            }

            //Pega o nome da tabela escolhida
            nomeTabela = Enum.GetName(typeof(Tabela), tabela);
            
            //Se escolheu "TODAS",então cria o array
            if (nomeTabela.ToUpper() == "TODAS") { ArrayTabelas = Enum.GetNames(typeof(Tabela)); }

            try
            {
                //Tenta conectar ao MDB
                conexaoAccess.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + ArquivoMDB + ";";
                conexaoAccess.Open();

                if (conexaoAccess.State == ConnectionState.Open)
                {
                    //Define a conexão ativa para o comando Access
                    comandoAccess.Connection = conexaoAccess;

                    //Limpando os dados
                    retorno = RemoveDadosAntigos(tabela);

                    //Define a string de conexão para o servidor de Desenvolvimento
                    //conexaoOracle.ConnectionString = @"Data Source=DES;User Id=corrmais;Password=corrwin;Integrated Security=no;"; //excluido - alias DES desativado - esertorio - 200809231211
                    //conexaoOracle.ConnectionString = @"Data Source=TOOLS;User Id=sinatools;Password=sinatools;Integrated Security=no;"; //incluido - esertorio - 200809231211

                    //Abrindo a conexao com o Oracle
                    conexaoOracle.Open();

                    //Cria a transação
                    transacaoOracle = conexaoOracle.BeginTransaction(IsolationLevel.ReadCommitted);

                    //Define a conexão do comando
                    comandoOracle.Connection = conexaoOracle;

                    //Define a transação
                    comandoOracle.Transaction = transacaoOracle;

                    //Define o tipo de comando a executar
                    comandoOracle.CommandType = CommandType.Text;
                    
                    //Se escolheu todas as tabelas,então migra uma a uma, senão será pontual
                    if (tabela == Tabela.Todas)
                    {
                        //Varre cada nome da tabela para fazer o DELETE (se fossepelo enum,seria até: Enum.GetValues(typeof(Tabelas)).Length-1
                        for (int i = 0; i < ArrayTabelas.Length - 1; i++)
                        {
                            //Define o comando
                            comandoAccess.CommandText = "SELECT * FROM " + ArrayTabelas[i].ToString();


                            //Executa o comando alimentando o DataReader
                            drAccess = comandoAccess.ExecuteReader();

                            //Varre o DataReader e insere dentro do Oracle
                            while (drAccess.Read())
                            {
                                querySQL = new StringBuilder(); //Alterado por laerte em 14/07/2008
                                colunas = "";
                                dados = "";
                                //Fim alteração do Laerte                                //Monta os valores
                                for (int j = 0; j < drAccess.FieldCount; j++)
                                {
                                    colunas += drAccess.GetName(j) + ",";
                                    dados += DadoFormatado(drAccess.GetFieldType(j), drAccess.GetValue(j).ToString()) + ",";
                                }
                                colunas = colunas.Remove(colunas.Length - 1, 1);
                                dados = dados.Remove(dados.Length - 1, 1);
                                dados = dados.Replace("\r", " ").Replace("\n", " ");

                                //Monta o Insert
                                querySQL.Append("INSERT INTO " + ArrayTabelas[i].ToString() + "(" + colunas + ") VALUES (" + dados + ")");
                                
                                //Insere os dados
                                comandoOracle.CommandText = querySQL.ToString();
                                comandoOracle.ExecuteNonQuery();
                            }
                            drAccess.Close();
                            //drAccess = null;//-->  Alterado por Laerte em 14/07/2008
                        }
                    }
                    else
                    {
                        //Define ocomando
                        comandoAccess.CommandText = "SELECT * FROM " + nomeTabela;

                        //Executa o comando alimentando o DataReader
                        drAccess = comandoAccess.ExecuteReader();

                        //Varre o DataReader e insere dentro do Oracle
                        while (drAccess.Read())
                        {
                            //Varre os campos para gerar o INSERT dinâmico
                            for (int j = 0; j < drAccess.FieldCount; j++) 
                            {
                                colunas += drAccess.GetName(j) + ",";
                                dados += DadoFormatado(drAccess.GetFieldType(j), drAccess.GetValue(j).ToString()) + ",";
                            }
                            colunas = colunas.Remove(colunas.Length - 1, 1);
                            dados = dados.Remove(dados.Length - 1, 1);
                            dados = dados.Replace("\r", " ").Replace("\n", " ");

                            //Monta o Insert
                            querySQL.Append(@"INSERT INTO " + nomeTabela + "(" + colunas + ") VALUES (" + dados + ")");

                            //Insere os dados
                            comandoOracle.CommandText = querySQL.ToString();
                            comandoOracle.ExecuteNonQuery();
                        }
                    }

                    //Valida a transação
                    transacaoOracle.Commit();

                    //Retorno OK
                    retorno = true;

                }
                else
                {
                    //Lança exceção avisando que não conseguiu abrir
                    throw new Exception("Erro ao tentar conectar ao arquivo .MDB!");
                }
            }
            catch(Exception ex)
            {
                //Da rollback na transação
                transacaoOracle.Rollback();
                //Avisa que deu erro
                retorno = false;
                throw new Exception("Erro ao tentar importar os dados do MDB para o Oracle!\n" + ex.Message.ToString(),ex.InnerException);
            }
            finally
            {
                //Libera os recursos
                if (comandoAccess != null) { comandoAccess.Dispose(); }
                if (conexaoAccess.State != ConnectionState.Closed) { conexaoAccess.Close(); }
                if (transacaoOracle != null) { transacaoOracle.Dispose(); }
                if (comandoOracle != null) { comandoOracle.Dispose(); }
                if (conexaoOracle.State != ConnectionState.Closed) { conexaoOracle.Close(); }
            }

            //Retorna o que ocorreu
            return retorno;
        }

        #endregion
    }
}
